home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / scheme / pcscheme / geneva / sources.exe / SAMPLES / MATRIX.S < prev    next >
Encoding:
Internet Message Format  |  1993-02-03  |  25.6 KB

  1. From news.unige.ch!scsing.switch.ch!ira.uka.de!sol.ctr.columbia.edu!usc!sdd.hp.com!ux1.cso.uiuc.edu!usenet.ucs.indiana.edu!master.cs.rose-hulman.edu!master.cs.rose-hulman.edu!news Fri Jan 22 13:30:52 1993
  2. Path: news.unige.ch!scsing.switch.ch!ira.uka.de!sol.ctr.columbia.edu!usc!sdd.hp.com!ux1.cso.uiuc.edu!usenet.ucs.indiana.edu!master.cs.rose-hulman.edu!master.cs.rose-hulman.edu!news
  3. From: eigenstr@cs.rose-hulman.edu (Todd R. Eigenschink)
  4. Newsgroups: comp.lang.scheme
  5. Subject: Matrix routines (kind of long)
  6. Date: 18 Jan 93 13:43:17
  7. Organization: Rose-Hulman Institute of Technology
  8. Lines: 943
  9. Distribution: comp
  10. Message-ID: <EIGENSTR.93Jan18134317@nyssa.cs.rose-hulman.edu>
  11. NNTP-Posting-Host: nyssa.cs.rose-hulman.edu
  12.  
  13. I've received several requests for my matrix routines.  I'm not done
  14. with them yet, and they're not perfect, and they're not even
  15. necessarily fast.  However, they do work (as far as I can tell).  I'd
  16. appreciate bug fixes and such, of course.  If anyone has anything
  17. they'd like to contribute, I'd like that, too.
  18.  
  19. Also, to the couple of people that I mailed the routines to
  20. earlier--the file was missing dot-product.  Everything would work
  21. fine except for matrix-*.
  22.  
  23. So, here it is:
  24.  
  25. ;;; matrix.ss
  26. ;;;
  27. ;;; Matrix manipulation routines.
  28. ;;;
  29. ;;; Winter Quarter 1992
  30. ;;; Rose-Hulman Institute of Technology
  31. ;;; Todd R. Eigenschink
  32. ;;;
  33. ;;; The ideas for which routines to include were borrowed
  34. ;;; from a similar package written by jpm@dartmouth.  I started from
  35. ;;; scratch on almost all the routines, writing them, and
  36. ;;; then adding every error check I could think of.  I added
  37. ;;; a number of routines that I thought were missing or could
  38. ;;; be improved.
  39.  
  40.  
  41. ;;; Routines included:
  42. ;;;
  43. ;;; (augment-matrix a b)
  44. ;;; (back-substitute a rhs)
  45. ;;; (choleski-factor a)
  46. ;;; (copy-matrix a)
  47. ;;; (dot-product a b)
  48. ;;; (forward-substitute a rhs)
  49. ;;; (gauss-inverse a)
  50. ;;; (gauss-solve a rhs)
  51. ;;; (generate-latex-matrix a)
  52. ;;; (hilbert-matrix n)
  53. ;;; (identity-matrix n)
  54. ;;; (make-matrix row-size col-size fn)
  55. ;;; (matrix-* a b)
  56. ;;; (matrix-+ a b)
  57. ;;; (matrix-- a b)
  58. ;;; (matrix-col a)
  59. ;;; (matrix-cols a)
  60. ;;; (matrix-op sym proc a b)
  61. ;;; (matrix-ref a i j)
  62. ;;; (matrix-row a)
  63. ;;; (matrix-rows a)
  64. ;;; (matrix-set! a i j value)
  65. ;;; (matrix-set-row! a i value)
  66. ;;; (matrix? a)
  67. ;;; (partial-pivot a)
  68. ;;; (permute-rows a perm-vec)
  69. ;;; (plu-factor m)
  70. ;;; (pretty-print-matrix a)
  71. ;;; (square? a)
  72. ;;; (transpose a)
  73. ;;; (tridiagonal-solve a rhs)
  74. ;;; (vector->matrix v)
  75.  
  76.  
  77.  
  78.  
  79. ;;; Principles:
  80. ;;;    1) a always refers to a matrix
  81. ;;;    2) i always refers to a row subscript
  82. ;;;    3) j always refers to a column subscript
  83. ;;;    4) Subscripts are (to the user) 1-based, just
  84. ;;;       like matrices in mathematics.
  85.  
  86.  
  87. ;;; You will notice that error checking is rather thorough.
  88. ;;; I chose to pay the speed penalty for evaluation so I was
  89. ;;; assured that programs crashed at the right time; I would
  90. ;;; rather get an error in matrix-* because the matrices are
  91. ;;; the wrong size than get an error from vector-ref because
  92. ;;; an index is out of range.  It's a lot easier to find
  93. ;;; errors when they come from the highest-level possible.
  94. ;;;
  95. ;;; The one error check that I consistently ignore is making
  96. ;;; sure that numeric operations (*, /, etc.) are performed
  97. ;;; on numbers; it's perfectly fine to create an arrays of
  98. ;;; strings, but just don't try to multiply them!
  99.  
  100.  
  101.  
  102.  
  103. ;;; Matrix primitives.  These are the only routines that
  104. ;;; have anything to do with how the matrices are
  105. ;;; defined (i.e., vectors of vectors, lists of lists, etc.).
  106. ;;;
  107. ;;; Matrices are stored as vectors of vectors.  The outer
  108. ;;; vector has length equal to the number of rows in the
  109. ;;; matrix.  Its elements are again vectors with length equal
  110. ;;; to the number of columns in the matrix.
  111. ;;;
  112. ;;; There are no restrictions on what can be placed in a
  113. ;;; matrix.  In pretty-print-matrix, for example, a matrix
  114. ;;; of strings is used.
  115.  
  116.  
  117.  
  118. ;;; matrix?
  119. ;;;
  120. ;;; Returns #t if a is a matrix; #f otherwise.
  121. ;;;
  122. ;;; First, make sure that the outer part is a vector,
  123. ;;; then make sure that the first element of that
  124. ;;; vector is again a vector.  Once both of those are
  125. ;;; true, make sure that every other element is a vector
  126. ;;; with the same length as the the first one.
  127.  
  128. (define (matrix? a)
  129.   (and (vector? a)
  130.        (vector? (vector-ref a 0))
  131.        (let ((l (vector-length (vector-ref a 0))))
  132.      (andmap (lambda (x) x)
  133.          (map (lambda (x)
  134.             (and (vector? x)
  135.                  (= (vector-length x) l)))
  136.               (vector->list a))))))
  137.  
  138.  
  139. ;;; make-matrix
  140. ;;;
  141. ;;; Creates a new matrix with the given dimensions.
  142. ;;; The element for each position is the result of
  143. ;;; the application of fn to the row and column
  144. ;;; values.
  145.  
  146. (define (make-matrix row-size col-size fn)
  147.   (cond
  148.    ((or (not (integer? row-size)) (< row-size 1))
  149.     (error 'make-matrix "row-size must be a positive integer"))
  150.    ((or (not (integer? col-size)) (< col-size 1))
  151.     (error 'make-matrix "col-size must be a positive integer"))
  152.    ((not (procedure? fn))
  153.     (error 'make-matrix "fn must be a procedure"))
  154.    (else
  155.     (do ((a (make-vector row-size))
  156.      (r (1- row-size) (1- r)))
  157.     ((= r -1) a)
  158.       (vector-set! a r (do ((v (make-vector col-size))
  159.                 (c (1- col-size) (1- c)))
  160.                ((= c -1) v)
  161.              (vector-set! v c (fn (1+ r) (1+ c)))))))))
  162.  
  163.  
  164. ;;; matrix-ref
  165. ;;;
  166. ;;; Returns the element a_ij.
  167.  
  168. (define (matrix-ref a i j)
  169.   (cond
  170.    ((not (matrix? a))
  171.     (error 'matrix-ref "~s is not a matrix" a))
  172.    ((or (< i 1) (> i (matrix-rows a)))
  173.     (error 'matrix-ref "row subscript ~s is out of range for matrix ~s" i a))
  174.    ((or (< j 1) (> j (matrix-cols a)))
  175.     (error 'matrix-ref
  176.        "column subscript ~s is out of range for matrix ~s" j a))
  177.    (else
  178.     (vector-ref (vector-ref a (1- i)) (1- j)))))
  179.  
  180.  
  181. ;;; matrix-set!
  182. ;;;
  183. ;;; Set!'s the a_ij element of the matrix to value.
  184.  
  185. (define (matrix-set! a i j value)
  186.   (cond
  187.    ((not (matrix? a))
  188.     (error 'matrix-set! "~s is not a matrix" a))
  189.    ((or (< i 1) (> i (matrix-rows a)))
  190.     (error 'matrix-ref "row subscript ~s is out of range for array ~s" i a))
  191.    ((or (< j 1) (> j (matrix-cols a)))
  192.     (error 'matrix-ref "column subscript ~s is out of range for array ~s" j a))
  193.    (else
  194.     (vector-set! (vector-ref a (1- i)) (1- j) value))))
  195.  
  196.  
  197. ;;; matrix-rows
  198. ;;;
  199. ;;; Returns the number of rows in the matrix.
  200.  
  201. (define (matrix-rows a)
  202.   (cond
  203.    ((not (matrix? a))
  204.     (error 'matrix-rows "~s is not a matrix" a))
  205.    (else
  206.     (vector-length a))))
  207.  
  208.  
  209. ;;; matrix-cols
  210. ;;;
  211. ;;; Returns the number of columns in the matrix.
  212.  
  213. (define (matrix-cols a)
  214.   (cond
  215.    ((not (matrix? a))
  216.     (error 'matrix-cols "~s is not a matrix" a))
  217.    (else
  218.     (vector-length (vector-ref a 0)))))
  219.  
  220.  
  221. ;;; matrix-row
  222. ;;;
  223. ;;; Returns the ith row of matrix a as a vector.
  224. ;;;
  225. ;;; Note: The return vector is 0-based!
  226.  
  227. (define (matrix-row a i)
  228.   (cond
  229.    ((not (matrix? a))
  230.     (error 'matrix-row "~s is not a matrix" a))
  231.    ((or (< i 1) (> i (matrix-rows a)))
  232.     (error 'matrix-row "row subscript is out of range for array ~s" a))
  233.    (else
  234.     (vector-ref a (1- i)))))
  235.  
  236.  
  237. ;;; matrix-col
  238. ;;;
  239. ;;; Returns the jth column of matrix a as a vector.
  240. ;;;
  241. ;;; Note: The vector is 0-based!
  242.  
  243. (define (matrix-col a j)
  244.   (cond
  245.    ((not (matrix? a))
  246.     (error 'matrix-col "~s is not a matrix" a))
  247.    ((or (< j 1) (> j (matrix-cols a)))
  248.     (error 'matrix-col "column subscript is out of range for array ~s" a))
  249.    (else
  250.     (list->vector (map (lambda (v) (vector-ref v (1- j)))
  251.                (vector->list a))))))
  252.  
  253.  
  254. ;;; matrix-set-row!
  255. ;;;
  256. ;;; Sets the given row of the given matrix
  257. ;;; to the given value.
  258.  
  259. (define (matrix-set-row! a i value)
  260.   (cond
  261.    ((not (matrix? a))
  262.     (error 'matrix-set-row! "~s is not a matrix" a))
  263.    ((or (< i 1) (> i (matrix-rows a)))
  264.     (error 'matrix-set-row! "row subscript ~s is out of range" i))
  265.    ((not (= (matrix-cols a) (vector-length value)))
  266.     (error 'matrix-set-row! "vector ~s is not of length ~s"
  267.        value (matrix-cols a)))
  268.    (else
  269.     (vector-set! a (1- i) value))))
  270.  
  271.  
  272.  
  273.  
  274.  
  275. ;;; Other matrix routines
  276. ;;;
  277. ;;; These are not dependent on the structure of the matrices.
  278. ;;;
  279. ;;; All the routines below should use only the routines above
  280. ;;; for information about the matrix itself.  Nothing should
  281. ;;; be added that depends on the implementation of the above
  282. ;;; procedures.
  283.  
  284.  
  285.  
  286. ;;; square?
  287. ;;;
  288. ;;; Returns #t if the given item is
  289. ;;; both a matrix and square, and #f if not.
  290.  
  291. (define (square? a)
  292.   (and (matrix? a)
  293.        (= (matrix-rows a ) (matrix-cols a))))
  294.  
  295.  
  296. ;;; copy-matrix
  297. ;;;
  298. ;;; Returns an identical copy of the given matrix.
  299.  
  300. (define (copy-matrix a)
  301.   (cond
  302.    ((not (matrix? a))
  303.     (error 'copy-matrix "~s is not a matrix" a))
  304.    (else (make-matrix (matrix-rows a)
  305.               (matrix-cols a)
  306.               (lambda (i j) (matrix-ref a i j))))))
  307.  
  308.  
  309. ;;; transpose
  310. ;;;
  311. ;;; Returns the transpose of the given matrix.
  312.  
  313. (define (transpose a)
  314.   (cond
  315.    ((not (matrix? a))
  316.     (error 'transpose "~s is not a matrix" a))
  317.    (else
  318.     (make-matrix (matrix-cols a)
  319.          (matrix-rows a)
  320.          (lambda (i j) (matrix-ref a j i))))))
  321.  
  322.  
  323. ;;; permute-rows
  324. ;;;
  325. ;;; Permutes the rows of matrix according to
  326. ;;; the permutation vector given.
  327.  
  328. (define (permute-rows a perm-vec)
  329.   (cond
  330.    ((not (matrix? a))
  331.     (error 'permute-rows "~s is not a matrix" a))
  332.    ((not (vector? perm-vec))
  333.     (error 'permute-rows "~s is not a vector" perm-vec))
  334.    ((not (= (vector-length perm-vec) (1+ (matrix-rows a))))
  335.     (error 'permute-rows "permutation vector ~s is the wrong length" perm-vec))
  336.    (else
  337.     (let ((new-a (copy-matrix a)))
  338.       (do ((i 1 (1+ i))) ((> i (matrix-rows a)))
  339.     (matrix-set-row! new-a i (matrix-row a (vector-ref perm-vec i))))
  340.       new-a))))
  341.  
  342.  
  343. ;;; vector->matrix
  344. ;;;
  345. ;;; Converts an n-vector into an n x 1 matrix
  346. ;;; so it can be used with these procedures.
  347.  
  348. (define (vector->matrix v)
  349.   (cond
  350.    ((not (vector? v))
  351.     (error 'vector->matrix "~s is not a vector" v))
  352.    (else
  353.     (make-matrix (vector-length v) 1
  354.          (lambda (i j) (vector-ref v (1- i)))))))
  355.  
  356.  
  357. ;;; hilbert-matrix
  358. ;;;
  359. ;;; Returns an n x n Hilbert matrix.
  360.  
  361. (define (hilbert-matrix n)
  362.   (cond
  363.    ((< n 0)
  364.     (error 'hilbert-matrix "matrix must have positive size"))
  365.    (else
  366.     (make-matrix n n (lambda (i j) (/ 1 (+ i j -1)))))))
  367.  
  368.  
  369. ;;; indentity-matrix
  370. ;;;
  371. ;;; Returns an n x n identity matrix.
  372.  
  373. (define (identity-matrix n)
  374.   (cond
  375.    ((< n 0)
  376.     (error 'identity-matrix "matrix must have positive size"))
  377.    (else
  378.     (make-matrix n n (lambda (i j) (if (= i j) 1 0))))))
  379.  
  380.  
  381. ;;; augment-matrix
  382. ;;;
  383. ;;; Augments matrix a by adding the columns of matrix
  384. ;;; b to its right side.  The two matrices must obviously
  385. ;;; have the same number of rows.  This is intended for
  386. ;;; use in Gaussian elimination, where multiple right-hand
  387. ;;; side vectors can be solved for at once.
  388. ;;;
  389. ;;; This was 'roxed almost completely from jpm's original
  390. ;;; code.  I just basically just added error-checking.
  391.  
  392. (define (augment-matrix a b)
  393.   (cond
  394.    ((not (matrix? a))
  395.     (error 'augment-matrix "~s is not a matrix" a))
  396.    ((not (matrix? b))
  397.     (error 'augment-matrix "~s is not a matrix" b))
  398.    ((not (= (matrix-rows a) (matrix-rows b)))
  399.     (error 'augment-matrix "matrices have differing numbers of rows"))
  400.    (else
  401.     (make-matrix (matrix-rows a)
  402.          (+ (matrix-cols a) (matrix-cols b))
  403.          (lambda (i j)
  404.            (if (<= j (matrix-cols a))
  405.                (matrix-ref a i j)
  406.                (matrix-ref b i (- j (matrix-cols a)))))))))
  407.  
  408.  
  409. ;;; gauss-inverse
  410. ;;;
  411. ;;; Computes the inverse of matrix a by using
  412. ;;; Gaussian elimination.  Of course, the matrix
  413. ;;; must be square.  The solution is solved by
  414. ;;; setting Ax = I, where I is the n x n identity
  415. ;;; matrix.
  416.  
  417. (define (gauss-inverse a)
  418.   (cond
  419.    ((not (matrix? a))
  420.     (error 'gauss-inverse "~s is not a matrix" a))
  421.    ((not (square? a))
  422.     (error 'gauss-inverse "cannot invert non-square matrix ~s" a))
  423.    (else
  424.     (gauss-solve a (identity-matrix (matrix-rows a))))))
  425.  
  426.  
  427. ;;; gauss-solve
  428. ;;;
  429. ;;; Solves a matrix equation using Gaussian elimination.
  430. ;;; There can be multiple right-hand side vectors; they
  431. ;;; must be in the form of a matrix with the same number
  432. ;;; of rows as the left-hand side matrix.
  433. ;;;
  434. ;;; The method used is simple; LUx = b, so set Ux = v, and
  435. ;;; then Lv = b.  Solve for v first using forward
  436. ;;; substitution, and then use backward substitution to get x.
  437.  
  438. (define (gauss-solve a rhs)
  439.   (cond
  440.    ((not (matrix? a))
  441.     (error 'gauss-solve "~s is not a matrix" a))
  442.    ((not (matrix? rhs))
  443.     (error 'gauss-solve "~s is not a matrix" rhs))
  444.    ((not (= (matrix-rows a) (matrix-rows rhs)))
  445.     (error 'gauss-solve "matrices have differing numbers of rows"))
  446.    (else
  447.     (let* ((PLU (PLU-factor a))
  448.        (P (car PLU))
  449.        (L (cadr PLU))
  450.        (U (cddr PLU)))
  451.       (back-substitute U (forward-substitute L (permute-rows rhs P)))))))
  452.  
  453.  
  454. ;;; back-substitute
  455. ;;;
  456. ;;; Performs back substitution on the (assumed
  457. ;;; upper-triangular) matrix using the right-hand side
  458. ;;; vectors (in matrix form).
  459.  
  460. (define (back-substitute a rhs)
  461.   (cond
  462.    ((not (matrix? a))
  463.     (error 'back-substitute "~s is not a matrix" a))
  464.    ((not (matrix? rhs))
  465.     (error 'back-substitute "~s is not a matrix" rhs))
  466.    (else
  467.     (let* ((rows (matrix-rows rhs))
  468.        (cols (matrix-cols rhs))
  469.        (m (make-matrix rows cols (lambda (i j) 0)))
  470.        (term
  471.         (lambda (i vec-n)
  472.           (if (= i rows)
  473.           (/ (matrix-ref rhs rows vec-n) (matrix-ref a rows rows))
  474.           (/ (- (matrix-ref rhs i vec-n)
  475.             (let loop ((sum 0) (j (1+ i)))
  476.               (cond
  477.                ((> j (matrix-cols a)) sum)
  478.                (else (loop (+ sum (* (matrix-ref a i j)
  479.                          (matrix-ref m j vec-n)))
  480.                        (1+ j))))))
  481.              (matrix-ref a i i))))))
  482.  
  483.       (do ((i rows (1- i))) ((= i 0))
  484.     (do ((j 1 (1+ j))) ((> j cols))
  485.       (matrix-set! m i j (term i j))))
  486.  
  487.       m))))
  488.  
  489.  
  490. ;;; forward-substitute
  491. ;;;
  492. ;;; Performs forward substitution on the (assumed
  493. ;;; upper-triangular) matrix and the righthand side vectors
  494. ;;; (in matrix form).  The method used is simple; just use
  495. ;;; backward substitution after mirroring the matrices.  The
  496. ;;; matrix itself has to be sort of flipped along the diagonal;
  497. ;;; the matrix of right-hand side vectors just has to be
  498. ;;; flipped upside-down.  Of course, once the set of solution
  499. ;;; vectors is found, it must be flipped back over.
  500.  
  501. (define (forward-substitute a rhs)
  502.   (cond
  503.    ((not (matrix? a))
  504.     (error 'forward-substitute "~s is not a matrix" a))
  505.    ((not (matrix? rhs))
  506.     (error 'forward-substitute "~s is not a matrix" rhs))
  507.    ((not (= (matrix-rows a) (matrix-rows rhs)))
  508.     (error 'forward-substitute "matrices have differing numbers of rows"))
  509.    (else
  510.     (let* ((rows (matrix-rows a))
  511.        (cols (matrix-cols a))
  512.        
  513.        ;; mirror-matrix 
  514.        (mm (lambda (a)
  515.          (make-matrix rows cols
  516.                   (lambda (i j)
  517.                 (matrix-ref
  518.                  a (- rows i -1) (- cols j -1))))))
  519.        ;; mirror-vector-matrix
  520.        (mvm (lambda (a)
  521.           (make-matrix rows (matrix-cols rhs)
  522.                    (lambda (i j)
  523.                  (matrix-ref a (- rows i -1) j))))))
  524.       
  525.       (mvm (back-substitute (mm a) (mvm rhs)))))))
  526.  
  527.  
  528. ;;; partial-pivot
  529. ;;;
  530. ;;; Performs partial pivoting, and returns the permutation
  531. ;;; matrix consed onto the new matrix.
  532.  
  533. (define (partial-pivot m)
  534.   (let* ((a (copy-matrix m))
  535.      (rows (matrix-rows a))
  536.      (cols (matrix-cols a))
  537.      (P (make-vector (1+ rows)))
  538.      (magic-d
  539.       (lambda (i j)
  540.         (let* ((row (vector->list (matrix-row a i))))
  541.           (/ (abs (matrix-ref a i j))
  542.          (apply max (map abs row))))))
  543.      (pivot
  544.       (lambda ()
  545.         (do ((level 1 (1+ level))) ((= level rows))
  546.           (let loop ((row (1+ level))
  547.              (max-row level)
  548.              (max-d (magic-d level level)))
  549.         (cond
  550.          ((> row (matrix-rows a))
  551.           (if (not (= max-row level))
  552.               (let ((temp-row (matrix-row a max-row))
  553.                 (temp-n (vector-ref P max-row)))
  554.             (matrix-set-row! a max-row (matrix-row a level))
  555.             (matrix-set-row! a level temp-row)
  556.             (vector-set! P max-row level)
  557.             (vector-set! P level temp-n))))
  558.          ((> (magic-d row level) max-d)
  559.           (loop (1+ row) row (matrix-ref a row level)))
  560.          (else
  561.           (loop (1+ row) max-row max-d))))))))
  562.  
  563.     ;; Create initial permutation vector
  564.     (do ((i 1 (1+ i))) ((> i rows))
  565.       (vector-set! P i i))
  566.     ;; Actually perform the pivoting
  567.     (pivot)
  568.     ;; Create the return value
  569.     (cons P a)))
  570.  
  571.  
  572. ;;; PLU-factor
  573. ;;;
  574. ;;; Returns the PLU factorization of the matrix.
  575. ;;; The permutation vector is consed onto the results
  576. ;;; of consing L onto U.
  577. ;;;
  578. ;;; Gaussian elimination with partial pivoting (rows only)
  579. ;;; is used to performs the factorization.  The results of
  580. ;;; switching rows are kept in a permutation vector which
  581. ;;; is returned as P in the PLU factorization.
  582.  
  583. (define (PLU-factor m)
  584.   (cond
  585.    ((not (matrix? m))
  586.     (error 'PLU-factor "~s is not a matrix" m))
  587.    ((not (= (matrix-rows m) (matrix-cols m)))
  588.     (error 'PLU-factor "cannot factor a non-square matrix"))
  589.    (else
  590.     (let* ((PM (partial-pivot m))
  591.        (a (cdr PM))
  592.        (P (car PM))
  593.        (rows (matrix-rows a))
  594.        (cols (matrix-cols a)))
  595.  
  596.       ;; Main loop.  Goes top to bottom and left to right,
  597.       ;; creating zeros as it goes.
  598.  
  599.       (do ((k 1 (1+ k))) ((= k rows))
  600.     (do ((i (1+ k) (1+ i))) ((> i rows))
  601.       (let ((m_ik (/ (matrix-ref a i k) (matrix-ref a k k))))
  602.         (matrix-set! a i k m_ik)
  603.         (do ((j (1+ k) (1+ j))) ((> j cols))
  604.           (matrix-set! a i j
  605.                (- (matrix-ref a i j)
  606.                   (* m_ik (matrix-ref a k j))))))))
  607.  
  608.       ;; Create the return value.  This returns separate L and U
  609.       ;; matrices by pulling the appropriate values out of a.
  610.       (cons P (cons (make-matrix rows cols
  611.                  (lambda (i j)
  612.                    (cond
  613.                     ((= i j) 1)
  614.                     ((> i j) (matrix-ref a i j))
  615.                     (else 0))))
  616.             (make-matrix rows cols
  617.                  (lambda (i j)
  618.                    (if (<= i j)
  619.                        (matrix-ref a i j) 0)))))))))
  620.  
  621.  
  622. ;;; doolittle-factor
  623. ;;;
  624. ;;; Factors the matrix into LU by using Doolittle's
  625. ;;; method.  Returns L consed onto U.
  626.  
  627. (define (doolittle-factor a)
  628.   (cond
  629.    ((not (matrix? a))
  630.     (error 'doolittle-factor "~s is not a matrix" a))
  631.    ((not (square? a))
  632.     (error 'doolittle-factor "matrix is not square"))
  633.    (else
  634.     (let* ((n (matrix-rows a))
  635.        (L (make-matrix
  636.            n n (lambda (i j) (if (= j 1) (matrix-ref a i 1) 0))))
  637.        (U (make-matrix
  638.            n n (lambda (i j) (if (= i 1)
  639.                      (/ (matrix-ref a 1 j)
  640.                     (matrix-ref L 1 1))
  641.                      0)))))
  642.  
  643.       (do ((j 2 (1+ j))) ((> j n))
  644.  
  645.     (do ((i j (1+ i))) ((> i n))
  646.  
  647.       (matrix-set!
  648.        L i j
  649.        (- (matrix-ref a i j)
  650.           (do ((k 1 (1+ k)) (sum 0))
  651.           ((= j k) sum)
  652.         (set! sum (+ sum (* (matrix-ref L i k)
  653.                     (matrix-ref U k j))))))))
  654.  
  655.     (matrix-set! U j j 1)
  656.  
  657.     (do ((i (1+ j) (1+ i)))
  658.         ((> i n))
  659.       (matrix-set!
  660.        U j i
  661.        (/ (- (matrix-ref a j i)
  662.          (do ((k 1 (1+ k)) (sum 0))
  663.              ((= k j) sum)
  664.            (set! sum (+ sum (* (matrix-ref L j k)
  665.                        (matrix-ref U k i))))))
  666.           (matrix-ref L j j)))))
  667.  
  668.       (cons L U)))))
  669.  
  670.  
  671. ;;; choleski-factor
  672. ;;;
  673. ;;; Factors the matrix into LU by using Choleski's
  674. ;;; method
  675.  
  676. (define (choleski-factor a)
  677.   (cond
  678.    ((not (matrix? a))
  679.     (error 'choleski-factor "~s is not a matrix" a))
  680.    ((not (square? a))
  681.     (error 'choleski-factor "matrix is not square"))
  682.    (else
  683.     (let* ((n (matrix-rows a))
  684.        (L (make-matrix n n (lambda (i j) 0))))
  685.  
  686.       (matrix-set! L 1 1 (sqrt (matrix-ref a 1 1)))
  687.  
  688.       (do ((j 2 (1+ j))) ((> j n))
  689.     (matrix-set! L j 1 (/ (matrix-ref a j 1) (matrix-ref L 1 1))))
  690.  
  691.       (do ((i 2 (1+ i))) ((= i n))
  692.     (matrix-set!
  693.      L i i
  694.      (sqrt (- (matrix-ref a i i)
  695.           (let loop ((k 1) (sum 0))
  696.             (cond
  697.              ((= k i) sum)
  698.              (else
  699.               (loop (1+ k) (+ sum (expt (matrix-ref L i k) 2)))))))))
  700.     
  701.     (do ((j (1+ i) (1+ j))) ((> j n))
  702.       (matrix-set!
  703.        L j i
  704.        (/ (- (matrix-ref a j i)
  705.          (let loop ((k 1) (sum 0))
  706.            (cond
  707.             ((= k i) sum)
  708.             (else
  709.              (loop (1+ k) (+ sum (* (matrix-ref L j k)
  710.                         (matrix-ref L i k))))))))
  711.           (matrix-ref L i i)))))
  712.  
  713.       (matrix-set!
  714.        L n n
  715.        (sqrt (- (matrix-ref a n n)
  716.         (let loop ((k 1) (sum 0))
  717.           (cond
  718.            ((= k n) sum)
  719.            (else
  720.             (loop (1+ k) (+ sum (expt (matrix-ref L n k) 2)))))))))
  721.  
  722.       (cons L (transpose L))))))
  723.  
  724.  
  725. ;;; matrix-*
  726. ;;;
  727. ;;; Multiplies two matrices.  The number of columns
  728. ;;; in the left-hand matrix must be the same as the
  729. ;;; number of rows in the right-side matrix.  This
  730. ;;; operation is defined in terms of the dot product,
  731. ;;; which makes the algorithm trivial.
  732.  
  733. (define (matrix-* a b)
  734.   (cond
  735.    ((not (matrix? a))
  736.     (error 'matrix-* "~s is not a matrix" a))
  737.    ((not (matrix? b))
  738.     (error 'matrix-* "~s is not a matrix" b))
  739.    ((not (= (matrix-cols a) (matrix-rows b)))
  740.     (error 'matrix-* "# colums in A do not match # rows in B"))
  741.    (else
  742.     (make-matrix (matrix-rows a)
  743.          (matrix-cols b)
  744.          (lambda (i j)
  745.            (dot-product (matrix-col b j)
  746.                 (matrix-row a i)))))))
  747.  
  748.  
  749. ;;; matrix-op
  750. ;;;
  751. ;;; Performs some operation on corresponding elements in
  752. ;;; two same-sized matrices, and returns a new matrix
  753. ;;; composed of the results of the operation.
  754.  
  755. (define (matrix-op sym proc a b)
  756.   (cond
  757.    ((not (matrix? a))
  758.     (error sym "~s is not a matrix" a))
  759.    ((not (matrix? b))
  760.     (error sym "~s is not a matrix" b))
  761.    ((not (= (matrix-cols a) (matrix-cols b)))
  762.     (error sym "matrices have differing numbers of columns"))
  763.    ((not (= (matrix-rows a) (matrix-rows b)))
  764.     (error sym "matrices have differing numbers of rows"))
  765.    (else
  766.     (make-matrix (matrix-rows a)
  767.          (matrix-cols a)
  768.          (lambda (i j)
  769.            (proc (matrix-ref a i j) (matrix-ref b i j)))))))
  770.  
  771.  
  772. ;;; matrix--
  773. ;;;
  774. ;;; Subtracts two matrices.  They must be of the same
  775. ;;; size, of course.  This trivially uses matrix-op.
  776.  
  777. (define (matrix-- a b)
  778.   (matrix-op 'matrix-- - a b))
  779.  
  780.  
  781. ;;; matrix-+
  782. ;;;
  783. ;;; Adds two matrices.  They must be of the same
  784. ;;; size, of course.  This trivially uses matrix-op.
  785.  
  786. (define (matrix-+ a b)
  787.   (matrix-op 'matrix-+ + a b))
  788.  
  789.  
  790.  
  791. ;;; tridiagonal-solve
  792. ;;;
  793. ;;; Solves a tridiagonal matrix.
  794.  
  795. (define (tridiagonal-solve a rhs)
  796.   (cond
  797.    ((not (matrix? a))
  798.     (error 'solve-tridiagonal "~s is not a matrix" a))
  799.    ((not (matrix? rhs))
  800.     (error 'solve-tridiagonal "~s is not a matrix" rhs))
  801.    ((not (square? a))
  802.     (error 'solve-tridiagonal "matrix is not square"))
  803.    (else
  804.     (let* ((n (matrix-rows a))
  805.        (get-a (lambda (k) (if (= k 1) 0 (matrix-ref a k (1- k)))))
  806.        (get-d (lambda (k) (matrix-ref a k k)))
  807.        (get-c (lambda (k) (if (= k n) 0 (matrix-ref a k (1+ k)))))
  808.        (set-d (lambda (k v) (matrix-set! a k k v)))
  809.        (x (make-matrix (matrix-rows rhs)
  810.                (matrix-cols rhs)
  811.                (lambda (i j) 0))))
  812.  
  813.       (do ((k 2 (1+ k))) ((> k n))
  814.  
  815.     (if (= (matrix-ref a (1- k) (1- k)) 0)
  816.         (error 'solve-tridiagonal "matrix cannot be solved"))
  817.  
  818.     (let ((m (/ (matrix-ref a k (1- k))
  819.             (matrix-ref a (1- k) (1- k)))))
  820.  
  821.       (set-d k (- (get-d k) (* m (get-c (1- k)))))
  822.  
  823.       (do ((j 1 (1+ j))) ((> j (matrix-cols rhs)))
  824.         (matrix-set!
  825.          rhs k j
  826.          (- (matrix-ref rhs k j) (* m (matrix-ref rhs (1- k) j)))))))
  827.  
  828.  
  829.       (if (= (matrix-ref a n n) 0)
  830.       (error 'solve-tridiagonal "matrix cannot be solved"))
  831.  
  832.  
  833.       (do ((j 1 (1+ j))) ((> j (matrix-cols rhs)))
  834.     (matrix-set! x n j (/ (matrix-ref rhs n j) (get-d n))))
  835.  
  836.  
  837.       (do ((k (1- n) (1- k))) ((= k 0))
  838.     (do ((j 1 (1+ j))) ((> j (matrix-cols rhs)))
  839.       (matrix-set!
  840.        x k j
  841.        (/ (- (matrix-ref rhs k j) (* (get-c k) (matrix-ref x (1+ k) j)))
  842.           (get-d k)))))
  843.  
  844.       x))))
  845.      
  846.  
  847.  
  848.  
  849.  
  850. ;;; pretty-print-matrix
  851. ;;;
  852. ;;; Prints a matrix in a decent format, with vertical
  853. ;;; bars on either side.
  854.  
  855. (define (pretty-print-matrix a)
  856.   (cond
  857.    ((not (matrix? a))
  858.     (error 'pretty-print-matrix "~s is not a matrix" a))
  859.    (else
  860.     (let* ((rows (matrix-rows a))
  861.        (cols (matrix-cols a))
  862.        (width-vec (make-vector (1+ cols)))
  863.        (s (make-matrix rows cols
  864.                (lambda (i j)
  865.                  (format "~a" (matrix-ref a i j))))))
  866.  
  867.       ;; Find the greatest width in each column
  868.       (do ((i 1 (1+ i))) ((> i rows))
  869.     (do ((j 1 (1+ j))) ((> j cols))
  870.       (let ((len (string-length (matrix-ref s i j))))
  871.         (if (> len (vector-ref width-vec j))
  872.         (vector-set! width-vec j len)))))
  873.       
  874.       ;; Now that the widths are computed, display the matrix,
  875.       ;; spacing as needed.
  876.       (do ((i 1 (1+ i))) ((> i rows))
  877.     (display "|")
  878.     (do ((j 1 (1+ j))) ((> j cols))
  879.       (let* ((obj (matrix-ref s i j))
  880.          (len (string-length obj)))
  881.         (printf " ~a~a "
  882.             (make-string (- (vector-ref width-vec j) len) #\ )
  883.             obj)))
  884.     (display "|")
  885.     (newline))))))
  886.  
  887.  
  888. ;;; generate-latex-matrix
  889. ;;;
  890. ;;; Generates LaTeX code for the matrix passed in
  891. ;;; which can be pasted into a LaTeX document.  This
  892. ;;; does not pay attention to how large the matrix is;
  893. ;;; it might be necessary to add code to change the
  894. ;;; font size if the matrix spills off the right side
  895. ;;; of the page.
  896.  
  897. (define (generate-LaTeX-matrix a)
  898.   (cond
  899.    ((not (matrix? a))
  900.     (error 'generate-LaTeX "~s is not a matrix" a))
  901.    (else
  902.     (let ((cols (matrix-cols a))
  903.       (rows (matrix-rows a)))
  904.  
  905.       ;; Turn on displayed math
  906.       (display "\\( \\left( \\begin{array}{")
  907.       (display (make-string cols #\c))
  908.       (display "}")
  909.       (newline)
  910.  
  911.       (do ((i 1 (1+ i))) ((> i rows))
  912.     (do ((j 1 (1+ j))) ((> j cols))
  913.       
  914.       (let ((el (matrix-ref a i j)))
  915.         
  916.         (cond
  917.          ;; Handles integers or inexact (floating-point) numbers
  918.          ((or (inexact? el) (and (exact? el) (integer? el)))
  919.           (display el))
  920.          ;; Handles fractions
  921.          (else
  922.           (printf "\\frac{~a}{~a}" (numerator el) (denominator el))))
  923.         
  924.         ;; Inter-column spacing
  925.         (if (< j cols) (display " & "))
  926.         
  927.         ;; End-of row command to go to new line,
  928.         ;; but not after the last row
  929.         (if (and (< i rows) (= j cols)) (display " \\\\"))
  930.         
  931.         ;; Pop in a newline after the last column in each row
  932.         (if (= j cols) (newline))))
  933.     
  934.     ;; Wrap it up
  935.     (display "\\end{array} \\right) \\)")
  936.     (newline))))))
  937.  
  938.  
  939. ;;; dot-product
  940. ;;;
  941. ;;; Returns the dot product of two vectors.
  942.  
  943. (define (dot-product x y)
  944.   (cond
  945.    ((not (vector? x))
  946.     (error 'dot-product "~s is not a vector" x)]
  947.    ((not (vector? y))
  948.     (error 'dot-product "~s is not a vector" y)]
  949.    ((not (= (vector-length x) (vector-length y)))
  950.     (error 'dot-product "vectors are of differing lengths")]
  951.    (else
  952.     (let loop ((r 0] (i 0])
  953.       (cond
  954.        ((= i (vector-length x)) r]
  955.        (else (loop (+ r (* (vector-ref x i) (vector-ref y i))) (1+ i))))))))
  956.  
  957.